package com.gitee.wsl.flow.sample

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.filterNot
import kotlinx.coroutines.flow.transformWhile


/**
 * These interfaces can be used to mark object you emit to a Flow as a special value.
 *
 * Extension methods to Flow can be used to filter and/or terminate on these values.
 */
sealed interface SpecialFlowValue {

    /**
     * Indicates this value is the last in a series.
     * This is useful in particular to terminate collection from endless flows such as SharedFlow and StateFlow.
     */
    interface Last : SpecialFlowValue

    /**
     * Indicates the value is less meaningful (e.g. an initial state with no value yet) that can usually be skipped
     */
    interface NotImportant : SpecialFlowValue
}

/**
 * Transforms a [Flow] to emit only up to the first [SpecialFlowValue.Last] received.
 * @param includeLast if `true` the first [SpecialFlowValue.Last] will be emitted before emission stops.
 * @return A flow that emits up to the first [SpecialFlowValue.Last] emitted by the original flow.
 */
fun <T:SpecialFlowValue> Flow<T>.takeUntilLast(includeLast: Boolean = true): Flow<T> = transformWhile {
    val notLast = it !is SpecialFlowValue.Last
    if (notLast || includeLast) {
        emit(it)
    }
    notLast
}

/**
 * Filters out all values marked as [SpecialFlowValue.NotImportant] from a given [Flow]
 * @return A flow that doesn't emit any elements implementing [SpecialFlowValue.NotImportant].
 */
@Suppress("NOTHING_TO_INLINE") // copies from filter()
inline fun <T:SpecialFlowValue> Flow<T>.filterOnlyImportant(): Flow<T> =
    filterNot { it is SpecialFlowValue.NotImportant }

/**
 * Collects all elements from a [Flow] up to the first [SpecialFlowValue.Last] received.
 * @param includeLast if `true` the first [SpecialFlowValue.Last] will be collected before emission stops.
 * @param collector The [FlowCollector] to collect the values.
 */
suspend inline fun <T:SpecialFlowValue> Flow<T>.collectUntilLast(includeLast: Boolean = true, collector: FlowCollector<T>) = takeUntilLast(includeLast).collect(collector)

/**
 * Collects all elements not implementing [SpecialFlowValue.NotImportant] from a [Flow].
 * @param collector The [FlowCollector] to collect the values.
 */
suspend inline fun <T:SpecialFlowValue> Flow<T>.collectImportant(collector: FlowCollector<T>) = filterOnlyImportant().collect(collector)

/**
 * Collects all elements not implementing [SpecialFlowValue.NotImportant] from a [Flow] up to the first [SpecialFlowValue.Last] received.
 * @param includeLast if `true` the first [SpecialFlowValue.Last] will be collected before emission stops.
 * @param collector The [FlowCollector] to collect the values.
 */
suspend inline fun <T:SpecialFlowValue> Flow<T>.collectImportantUntilLast(includeLast: Boolean = true, collector: FlowCollector<T>) =
    takeUntilLast(includeLast).filterOnlyImportant().collect(collector)
